#ifndef _GALOIS_H
#define _GALOIS_H

#include <stdint.h>
#include <vector>
using namespace std;

typedef vector <int> sIVec;
typedef vector <sIVec> sIIVec;

//Abstract class
class Galois {
	public:
		int w;

		virtual int galois_inverse(int a) =0;
		virtual int galois_single_multiply(int a, int b) =0;
		virtual int galois_single_divide(int a, int b) =0;
		virtual void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add) =0;
};

class Galois_w08: public Galois {
	public:
		virtual int galois_inverse(int a) =0;
		virtual int galois_single_multiply(int a, int b) =0;
		virtual int galois_single_divide(int a, int b) =0;
		virtual void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add) =0;
};

class Galois_w16: public Galois {
	public:
		virtual int galois_inverse(int a) =0;
		virtual int galois_single_multiply(int a, int b) =0;
		virtual int galois_single_divide(int a, int b) =0;
		virtual void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add) =0;
};

class Galois_w32: public Galois {
	public:
		virtual int galois_inverse(int a) =0;
		virtual int galois_single_multiply(int a, int b) =0;
		virtual int galois_single_divide(int a, int b) =0;
		virtual void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add) =0;
};

class Galois_Table_w08: public Galois_w08 {
	public:
		unsigned char *galois_w08_mult_table;
		unsigned char *galois_w08_div_table;

		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Table_w08() {}
		Galois_Table_w08(int wtmp);
};

class Galois_Table_w16: public Galois_w16 {
	public:
		uint16_t *galois_w16_mult_table;
		uint16_t *galois_w16_div_table;

		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Table_w16() {}
		Galois_Table_w16(int wtmp);
};

class Galois_Log_w08: public Galois_w08 {
	public:
		unsigned char *galois_w08_log_table;
		unsigned char *galois_w08_ilog_table;

		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Log_w08() {}
		Galois_Log_w08(int wtmp);
};

class Galois_Log_w16: public Galois_w16 {
	public:
		uint16_t *galois_w16_log_table;
		uint16_t *galois_w16_ilog_table;

		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Log_w16() {}
		Galois_Log_w16(int wtmp);
};

class Galois_Log_w32: public Galois_w32 {
	public:
		uint32_t *galois_w32_log_table;
		uint32_t *galois_w32_ilog_table;
		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Log_w32() {}
		Galois_Log_w32(int wtmp);
};

class Galois_Shift_w32: public Galois_w32 {
	public:
		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Shift_w32() {}
		Galois_Shift_w32(int wtmp);
};

class Galois_Split_w32: public Galois_w32 {
	public:
		uint32_t **galois_split_w08_tables;
		int galois_inverse(int a);
		int galois_single_multiply(int a, int b);
		int galois_single_divide(int a, int b);
		void galois_region_multiply(unsigned char *region, int multby, int nbytes, unsigned char *r2, int add);
		Galois_Split_w32() {}
		Galois_Split_w32(int wtmp);
};

/* Choices for type
	-1 == choose by assumed optimal
	 0 == table
	 1 == log
	 2 == shift
	 3 == split tables (32 only) */
Galois *galois_init(int w, int type);

int galois_create_mult_tables(int w, unsigned char **galois_mult_tables, unsigned char **galois_div_tables);
int galois_create_mult_tables(int w, uint16_t **galois_mult_tables, uint16_t **galois_div_tables);

int galois_create_log_tables(int w, unsigned char **galois_log_tables, unsigned char **galois_ilog_tables);
int galois_create_log_tables(int w, uint16_t **galois_log_tables, uint16_t **galois_ilog_tables);
int galois_create_log_tables(int w, uint32_t **galois_log_tables, uint32_t **galois_ilog_tables);

int galois_create_split_w08_tables(uint32_t **galois_split_w8);

void galois_region_xor(unsigned char *r1, unsigned char *r2, unsigned char *r3, int nbytes);

void galois_invert_binary_matrix(int *mat, int *inv, int rows);

void reed_sol_galois_w08_region_multby_2(unsigned char *region, int nbytes);
void reed_sol_galois_w16_region_multby_2(unsigned char *region, int nbytes);
void reed_sol_galois_w32_region_multby_2(unsigned char *region, int nbytes);

#endif
